﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyBBS.Data.Interfaces;
using MyBBS.Data.Models;
using MyBBS.Data.Filters;
using MyBBS.Data.Extensions;

namespace MyBBS.Data
{
    public class PostRepository : IPostRepository
    {
        MyBBS.Data.SqlServer.PostDataContext db;

        //public PostRepository()
        //    : this(new MyBBS.Data.SqlServer.PostDataContext())
        //{
        //}

        public PostRepository(MyBBS.Data.SqlServer.PostDataContext dataContext)
        {
            db = dataContext;
            if (db == null)
                throw new InvalidOperationException("DataContext should not be null");
        }


        #region IPostRepository Members

        /// <summary>
        /// Get all posts with their comment
        /// </summary>
        /// <returns>IQueryable Post</returns>
        public IQueryable<Post> GetPosts()
        {
            var posts = (from p in db.Posts
                         let comments = GetComments().WithPostIDEquals(p.ID)
                         let kicks = GetKicks().WithPostIDEquals(p.ID)
                         orderby p.Kicks.Count descending
                         select new Post
                         {
                             ID = p.ID,
                             CategoryID = p.CategoryID,
                             URL = p.URL,
                             Title = p.Title,
                             Description = p.Description,
                             Author = p.Author,
                             PostedTime = p.PostedTime,
                             Kicks = new LazyList<Kick>(kicks),
                             Comments = new LazyList<Comment>(comments)
                         }
                         );

            return posts;
        }

        /// <summary>
        /// Get all comments
        /// </summary>
        /// <returns>IQueryable Comment</returns>
        public IQueryable<Comment> GetComments()
        {
            return (from c in db.Comments
                    orderby c.PostID, c.SubmitTime
                    select new Comment
                    {
                        ID = c.ID,
                        Content = c.Content,
                        PostID = c.PostID,                        
                        SubmitTime = c.SubmitTime,
                        Author = c.Author
                    }).AsQueryable<Comment>();
        }

        /// <summary>
        /// Get top 50 popular tags
        /// </summary>
        /// <returns></returns>
        public IQueryable<Tag> GetPopularTags()
        {
            var popularTags = (
                    from pt in db.PostTags
                    group pt by pt.TagID into g
                    orderby g.Count() descending
                    select new
                    {
                        TagID = g.Key,
                        Count = g.Count()
                    }
                   ).Take(50);


            return (
                    from pt in popularTags
                    join t in db.Tags on pt.TagID equals t.ID
                    where t.IsActive == true
                    orderby pt.Count descending
                    select new Tag
                    {
                        ID = pt.TagID,
                        TagName = t.TagName,
                        IsActive = true
                    }
                    );
        }

        /// <summary>
        /// Get all active categories
        /// </summary>
        /// <returns>IQueryable Category</returns>
        public IQueryable<MyBBS.Data.Models.Category> GetCategories()
        {
            return (
                    from c in db.Categories
                    where c.IsActive == true
                    select new MyBBS.Data.Models.Category
                    {
                        CategoryName = c.CategoryName,
                        ID = c.ID,
                        IsActive = true
                    }
                   ).AsQueryable<MyBBS.Data.Models.Category>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IQueryable<PostTag> GetPostTags()
        {
            return from pt in db.PostTags
                   select new PostTag
                   {
                       ID = pt.ID,
                       PostID = pt.PostID,
                       TagID = pt.TagID
                   };

        }

        /// <summary>
        /// Get all kicks
        /// </summary>
        /// <returns></returns>
        public IQueryable<Kick> GetKicks()
        {
            return from k in db.Kicks
                   select new Kick
                   {
                       ID = k.ID,
                       PostID = k.PostID,
                       UserID = k.UserID
                   };
        }


        /// <summary>
        /// Add a new post
        /// </summary>
        /// <param name="post">Post</param>
        public long AddPost(Post post)
        {
            var category = GetCategories().SingleOrDefault(c => c.ID == post.CategoryID);
            if (category == null)
            {
                throw new InvalidOperationException("Specified category doesn't exist");
            }

            var oldOne = GetPosts().SingleOrDefault(p => p.URL == post.URL);
            if (oldOne != null)
            {
                throw new InvalidOperationException("Specified post already exist");
            }

            MyBBS.Data.SqlServer.Post _post = new MyBBS.Data.SqlServer.Post()
                {
                    URL = post.URL,
                    Author = post.Author,
                    Description = post.Description.ContentEncode(),
                    Title = post.Title,
                    PostedTime = System.DateTime.Now,
                    CategoryID = post.CategoryID
                };

            db.Posts.InsertOnSubmit(_post);
            db.SubmitChanges();

            post.ID = _post.ID;

            AddTags(post);
            AddUserPost(post);

            return _post.ID;
        }

        /// <summary>
        /// Add a new comment
        /// </summary>
        /// <param name="comment">Comment</param>
        /// <returns>Comment ID</returns>
        public long AddComment(Comment comment)
        {
            Post post = GetPosts().WithIDEquals(comment.PostID).Single();
            MyBBS.Data.SqlServer.Comment _comment = new MyBBS.Data.SqlServer.Comment() 
                { 
                    PostID = comment.PostID, 
                    Author = comment.Author, 
                    Content = comment.Content.ContentEncode(), 
                    SubmitTime = DateTime.Now 
                };
            if (post != null)
            {
                db.Comments.InsertOnSubmit(_comment);
            }

            db.SubmitChanges();

            return _comment.ID;
        }

        /// <summary>
        /// Kick a post and return the post's kick count
        /// </summary>
        /// <param name="postID"></param>
        /// <returns></returns>
        public int Kick(Kick kick)
        {
            SqlServer.Kick _kick = new MyBBS.Data.SqlServer.Kick()
            {
                PostID = kick.PostID,
                UserID = kick.UserID,
                TimeStamp = DateTime.Now
            };

            db.Kicks.InsertOnSubmit(_kick);
            db.SubmitChanges();

            return GetKicks().CountKick(kick.PostID);
        }

        /// <summary>
        /// Add a new post's tags to db
        /// </summary>
        /// <param name="post">The new post</param>
        private void AddTags(Post post)
        {
            string[] _tags = post.Tags.Split(',');
            if (_tags.Length > 0)
            {
                var newTags = new List<SqlServer.Tag>();
                var allTags = new List<SqlServer.Tag>();

                for (int i = 0; i < _tags.Length; i++)
                {
                    string tagName = _tags[i];
                    if (!string.IsNullOrEmpty(tagName))
                    {
                        var tag = GetPopularTags().SingleOrDefault(t => t.TagName == tagName);
                        if (tag == null) // the tag is not existed
                        {
                            var newTag = new SqlServer.Tag();
                            newTag.TagName = tagName;
                            newTag.IsActive = true;
                            newTags.Add(newTag);
                            allTags.Add(newTag);
                        }                       
                        else
                            allTags.Add(new SqlServer.Tag() { ID = tag.ID, TagName = tag.TagName, IsActive = true });
                    }
                }
                //insert the tag to the Tag
                if (newTags.Count > 0)
                {
                    try
                    {
                        db.Tags.InsertAllOnSubmit(newTags);
                        db.SubmitChanges();
                    }
                    catch (Exception ex)
                    {
                        string msg = ex.Message;
                    }
                }
                //insert to the PostTag
                if (allTags.Count > 0)
                {
                    var postTags = new List<SqlServer.PostTag>();
                    foreach (SqlServer.Tag tag in allTags)
                    {
                        postTags.Add(new SqlServer.PostTag() { TagID = tag.ID, PostID = post.ID });
                    }
                    if (postTags.Count > 0)
                    {
                        try
                        {
                            db.PostTags.InsertAllOnSubmit(postTags);
                            db.SubmitChanges();
                        }
                        catch (Exception ex)
                        {
                            string msg = ex.Message;
                        }
                    }
                }

            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="post"></param>
        private void AddUserPost(Post post)
        {
            SqlServer.UserPost userPost = new MyBBS.Data.SqlServer.UserPost()
                {
                    PostID = post.ID,
                    UserID = new UserRepository(this.db).GetUserByUserName(post.Author).ID
                };

            db.UserPosts.InsertOnSubmit(userPost);
            db.SubmitChanges();
        }     

       
        #endregion
    }
}
